package self.zhangkang.chart.service.index

import self.zhangkang.chart.data.KLineInfo
import self.zhangkang.chart.data.MACD

/**
 * @author : Android-张康
 * created on: 2023/5/8 15:06
 * description: MACD 计算工具类
 *
 * @param nFast n快日的指数移动平均值
 * @param nSlow n慢日的指数移动平均值
 * @param p 黄线（DEA线）是黑线（DIF线）的p日平滑移动平均线
 */
class MACDTools(private val nFast: Int, private val nSlow: Int, private val p: Int) {

    companion object {
        //MACD参数
        private const val SHORT = 12
        private const val LONG = 26
        private const val M = 9

        fun calculationMACD(dataList: MutableList<KLineInfo>) {
            val macdTools = MACDTools(SHORT, LONG, M)
            var yesterdayMACD: MACD? = null
            dataList.forEachIndexed { index, kLineInfo ->
                val macd = macdTools.calculationMACD(
                    todayClosePrice = kLineInfo.closePrice,
                    yesterdayMACD = yesterdayMACD
                )
                dataList[index] = kLineInfo.copy(macd = macd)
                yesterdayMACD = macd
            }
        }
    }


    /**
     * 计算MACD
     *
     * @param todayClosePrice 今日收盘价
     * @param yesterdayMACD 昨日的MACD
     */
    fun calculationMACD(todayClosePrice: Float, yesterdayMACD: MACD?): MACD {
        if (null == yesterdayMACD) {
            return MACD(todayClosePrice, todayClosePrice, 0F, 0F, 0F)
        }
        val fastEMA = todayEMA(nFast, todayClosePrice, yesterdayMACD.fastEMA)
        val slowEMA = todayEMA(nSlow, todayClosePrice, yesterdayMACD.slowEMA)
        val DIF = DIF(fastEMA, slowEMA)
        val DEA = DEA(p, DIF, yesterdayMACD.DEA)
        val MACD = MACD(DIF, DEA)
        return MACD(fastEMA = fastEMA, slowEMA = slowEMA, DIF = DIF, DEA = DEA, MACD = MACD)
    }

    /**
     * EMA 加权指数移动平均值EMA (n) = [ 2/（n+1）] * 今日收盘价 + [（n-1）/（n+1）] * 昨日EMA (n) ；
     * 由该公式可知，数据必须从新股上市首日开始算起，不能从中间某个时间开始计算；
     * 新股上市首日，DIF=0，DEA=0，MACD=0；
     * 新股上市的下一交易日（即：第二个交易日），在计算EMA时，该EMA需要用上一交易日收盘价（而非0）来计算。
     */
    private fun todayEMA(n: Int, todayClosePrice: Float, yesterdayEMA: Float): Float {
        return (2.0F / (n + 1)) * todayClosePrice + ((n - 1) / (n + 1.0F)) * yesterdayEMA
    }

    /**
     * DIF 差离值
     * DIF=EMA（n快）- EMA（n慢）
     */
    private fun DIF(fastEMA: Float, slowEMA: Float): Float {
        return fastEMA - slowEMA
    }

    /**
     * DEA (p) = 今日DIF *【2/（p+1）】 + 前一日DEA * 【（p-1）/（p+1）】；
     */
    private fun DEA(p: Int, todayDIF: Float, yesterdayDEA: Float): Float {
        return todayDIF * (2.0F / (p + 1)) + yesterdayDEA * ((p - 1.0F) / (p + 1))
    }

    /**
     * MACD值
     * MACD=2 * (今日DIF－今日DEA) ;
     */
    private fun MACD(DIF: Float, DEA: Float): Float {
        return 2 * (DIF - DEA)
    }
}